#include "Character.h"
#include "Game.h"
#include <sstream>
#include <d3dx9math.h>

Character::Character(int CharacterID) {

	ostringstream message;

	message << "New Character with id " << CharacterID << " created!";

	Logger::Log(message.str(), __FILE__, __LINE__);

	// Initialize the Character with data from the Character table
	sqlite3* db;
	sqlite3_stmt* sqlQuery;
	int errcode;

	// connect to game database
	errcode = sqlite3_open("gamedb.s3db", &db);

	if(errcode) {
		Logger::Log(E_DB_CONNECT_FAILED, "SQL ERROR: Failed to open database", __FILE__, __LINE__, LOG_ERROR);
		MessageBox(NULL, L"Failed to open database", L"ERROR", MB_OK | MB_ICONERROR);
		sqlite3_close_v2(db);
	}

	ostringstream statement;

	statement << "SELECT * FROM game_characters WHERE id = " << CharacterID << ";"; 

	if(sqlite3_prepare_v2(db, statement.str().c_str(), -1, &sqlQuery, NULL) != SQLITE_OK) Logger::Log(E_DB_QUERY_FAILED, "SQL ERROR: Failed to prepare query", __FILE__, __LINE__, LOG_ERROR);

	this->CharacterData.dbId = CharacterID;
	this->CharacterData.creationTime = GetTickCount();

	if(sqlQuery != NULL) {
		while(sqlite3_step(sqlQuery) == SQLITE_ROW) {
			this->CharacterData.name = (char*)sqlite3_column_text(sqlQuery, 1);
			this->CharacterData.x = sqlite3_column_double(sqlQuery, 2);
			this->CharacterData.y = sqlite3_column_double(sqlQuery, 3);
			this->CharacterData.hp = sqlite3_column_double(sqlQuery, 4);
			this->CharacterData.invulnerable = (sqlite3_column_int(sqlQuery, 5) != 0)? true:false;
			this->CharacterData.CharacterTypeID = sqlite3_column_int(sqlQuery, 6);
			this->CharacterData.levelID = sqlite3_column_int(sqlQuery, 7);
			this->CharacterData.rotX = sqlite3_column_double(sqlQuery, 8);
			this->CharacterData.rotY = sqlite3_column_double(sqlQuery, 9);
			this->CharacterData.rotZ = sqlite3_column_double(sqlQuery, 10);
			this->CharacterData.xvel = sqlite3_column_double(sqlQuery, 11);
			this->CharacterData.yvel = sqlite3_column_double(sqlQuery, 12);
			this->CharacterData.isPlayer = (sqlite3_column_int(sqlQuery, 13) != 0)? true:false;
			this->CharacterData.lifespan = sqlite3_column_int(sqlQuery, 14);
			this->CharacterData.hookCharacterDbId = sqlite3_column_int(sqlQuery, 15);

		}
	}

	sqlite3_finalize(sqlQuery);

	statement.str("");
	statement.clear();

	statement << "SELECT * FROM character_types WHERE id = " << this->CharacterData.CharacterTypeID << ";";

	if(sqlite3_prepare_v2(db, statement.str().c_str(), -1, &sqlQuery, NULL) != SQLITE_OK) Logger::Log(E_DB_QUERY_FAILED, "SQL ERROR: Failed to prepare query", __FILE__, __LINE__, LOG_ERROR);

	if(sqlQuery != NULL) {
		while(sqlite3_step(sqlQuery) == SQLITE_ROW) {
			this->CharacterData.textureFile = (char*)sqlite3_column_text(sqlQuery, 2);
			this->CharacterData.isSolid = (sqlite3_column_int(sqlQuery, 3) != 0)? true:false;
			this->CharacterData.hasCollision = (sqlite3_column_int(sqlQuery, 4) != 0)? true:false;
			this->CharacterData.width = sqlite3_column_int(sqlQuery, 5);
			this->CharacterData.height = sqlite3_column_int(sqlQuery, 6);
			this->CharacterData.ticksPerFrame = sqlite3_column_int(sqlQuery, 7);
			this->CharacterData.isAnimated = (sqlite3_column_int(sqlQuery, 8) != 0)? true:false;
		}
	}

	this->texUID = Game::GraphicsPtr->LoadCharacterTexture(this->CharacterData.textureFile);

	sqlite3_finalize(sqlQuery);

	sqlite3_close_v2(db);

	this->hookCharacterId = -1;

	this->facingTo = Direction::Right;

	// Define the quad to where the sprite will be rendered

	this->CharacterData.textureResolution = 64; // texture resolution is 64x64 pixels

	this->FillQuad(this->CharacterData.width, this->CharacterData.height);

}

Character::Character(int CharacterTypeID, double xpos, double ypos, double velX, double velY, int lifespan) {
	ostringstream message;

	message << "New Character with CharacterTypeID " << CharacterTypeID << " created!";

	Logger::Log(message.str(), __FILE__, __LINE__);

	// Initialize the Character with data from the Character table
	sqlite3* db;
	sqlite3_stmt* sqlQuery;
	int errcode;

	// connect to game database
	errcode = sqlite3_open("gamedb.s3db", &db);

	if(errcode) {
		Logger::Log(E_DB_CONNECT_FAILED, "SQL ERROR: Failed to open database", __FILE__, __LINE__, LOG_ERROR);
		MessageBox(NULL, L"Failed to open database", L"ERROR", MB_OK | MB_ICONERROR);
		sqlite3_close_v2(db);
	}

	ostringstream statement;

	this->CharacterData.hp = 0;
	this->CharacterData.dbId = 0;
	this->CharacterData.isPlayer = false;
	this->CharacterData.invulnerable = false;
	this->CharacterData.levelID = 0;
	this->CharacterData.name = "";
	this->CharacterData.rotX = 0;
	this->CharacterData.rotY = 0;
	this->CharacterData.rotZ = 0;
	this->CharacterData.hookCharacterDbId = -1;

	this->CharacterData.CharacterTypeID = CharacterTypeID;
	this->CharacterData.x = xpos;
	this->CharacterData.y = ypos;
	this->CharacterData.xvel = velX;
	this->CharacterData.yvel = velY;
	this->CharacterData.creationTime = GetTickCount();
	this->CharacterData.lifespan = ((FLOAT)lifespan / 1000.0f) * ticksPS;

	statement << "SELECT * FROM character_types WHERE id = " << this->CharacterData.CharacterTypeID << ";";

	if(sqlite3_prepare_v2(db, statement.str().c_str(), -1, &sqlQuery, NULL) != SQLITE_OK) Logger::Log(E_DB_QUERY_FAILED, "SQL ERROR: Failed to prepare query", __FILE__, __LINE__, LOG_ERROR);

	if(sqlQuery != NULL) {
		while(sqlite3_step(sqlQuery) == SQLITE_ROW) {
			this->CharacterData.textureFile = (char*)sqlite3_column_text(sqlQuery, 2);
			this->CharacterData.isSolid = (sqlite3_column_int(sqlQuery, 3) != 0)? true:false;
			this->CharacterData.hasCollision = (sqlite3_column_int(sqlQuery, 4) != 0)? true:false;
			this->CharacterData.width = sqlite3_column_int(sqlQuery, 5);
			this->CharacterData.height = sqlite3_column_int(sqlQuery, 6);
			this->CharacterData.ticksPerFrame = sqlite3_column_int(sqlQuery, 7);
			this->CharacterData.isAnimated = (sqlite3_column_int(sqlQuery, 8) != 0)? true:false;
		}
	}

	this->texUID = Game::GraphicsPtr->LoadCharacterTexture(this->CharacterData.textureFile);

	sqlite3_finalize(sqlQuery);

	sqlite3_close_v2(db);

	this->hookCharacterId = -1;

	this->facingTo = Direction::Right;

	// Define the quad to where the sprite will be rendered

	this->CharacterData.textureResolution = 64; // texture resolution is 64x64 pixels

	this->FillQuad(this->CharacterData.width, this->CharacterData.height);
}

Character::~Character() {

	ostringstream message;

	message << "Character with id " << this->CharacterData.dbId << " destroyed!";

	Logger::Log(message.str(), __FILE__, __LINE__);
}

void Character::InitializeCharacterId(int pos) {
	this->uID = pos;
}

void Character::FillQuad(int width, int height) {
	D3DXIMAGE_INFO imageInfo;

	this->CharacterQuad[0].Color = D3DXCOLOR(0.5f, 0.5f, 0.0f, 1.0f);
	this->CharacterQuad[1].Color = D3DXCOLOR(0.5f, 0.5f, 0.0f, 1.0f);
	this->CharacterQuad[2].Color = D3DXCOLOR(0.5f, 0.5f, 0.0f, 1.0f);
	this->CharacterQuad[3].Color = D3DXCOLOR(0.5f, 0.5f, 0.0f, 1.0f);

	boost::filesystem::path filepath = boost::filesystem::complete(("Textures/" + this->CharacterData.textureFile).c_str());

	if(FAILED(D3DXGetImageInfoFromFile((TCHAR*)filepath.c_str(), &imageInfo))) {
		this->CharacterQuad[0].U = 0.0f;
		this->CharacterQuad[0].V = 0.0f;
		
		this->CharacterQuad[1].U = 1.0f;
		this->CharacterQuad[1].V = 0.0f;
		
		this->CharacterQuad[2].U = 0.0f;
		this->CharacterQuad[2].V = 1.0f;
		
		this->CharacterQuad[3].U = 1.0f;
		this->CharacterQuad[3].V = 1.0f;

		this->CharacterData.textureWidth = width;
		this->CharacterData.textureHeight = height;
	}
	else {
		this->CharacterQuad[0].U = 0.0f;
		this->CharacterQuad[0].V = 0.0f;
		
		this->CharacterQuad[1].U = 1.0f * ((FLOAT)width / (FLOAT)imageInfo.Width);
		this->CharacterQuad[1].V = 0.0f;
		
		this->CharacterQuad[2].U = 0.0f;
		this->CharacterQuad[2].V = 1.0f * ((FLOAT)height / (FLOAT)imageInfo.Height);
		
		this->CharacterQuad[3].U = 1.0f * ((FLOAT)width / (FLOAT)imageInfo.Width);
		this->CharacterQuad[3].V = 1.0f * ((FLOAT)height / (FLOAT)imageInfo.Width);

		this->CharacterData.textureWidth = imageInfo.Width;
		this->CharacterData.textureHeight = imageInfo.Height;
	}

	this->CharacterQuad[0].Z = 0.0f;
	this->CharacterQuad[1].Z = 0.0f;
	this->CharacterQuad[2].Z = 0.0f;
	this->CharacterQuad[3].Z = 0.0f;

	float truewidth = (float)width / this->CharacterData.textureResolution;
	float trueheight = (float)height / this->CharacterData.textureResolution;

	this->CharacterQuad[0].X = 0.0f - (truewidth / 2.0f);
	this->CharacterQuad[0].Y = 0.0f + (trueheight / 2.0f);

	this->CharacterQuad[1].X = 0.0f + (truewidth / 2.0f);
	this->CharacterQuad[1].Y = 0.0f + (trueheight / 2.0f);

	this->CharacterQuad[2].X = 0.0f - (truewidth / 2.0f);
	this->CharacterQuad[2].Y = 0.0f - (trueheight / 2.0f);

	this->CharacterQuad[3].X = 0.0f + (truewidth / 2.0f);
	this->CharacterQuad[3].Y = 0.0f - (trueheight / 2.0f);

	this->animDirection = NoAnimation;
	this->totAnimSteps = this->CharacterData.textureWidth/this->CharacterData.width;
	this->animationStep = 0;
	this->runningAnimationDirection = NoAnimation;
	this->lastFrame = 51;
}

Direction Character::GetFacingToDirection() {
	return this->facingTo;
}

int Character::GetDBId() {
	return this->CharacterData.dbId;
}

int Character::GetUId() {
	return this->uID;
}

double Character::GetCX() {
	return this->CharacterData.x;
}

double Character::GetCY() {
	return this->CharacterData.y;
}

double Character::GetVX() {
	return this->CharacterData.xvel;
}

double Character::GetVY() {
	return this->CharacterData.yvel;
}

double Character::GetYaw() {
	return D3DXToRadian(this->CharacterData.rotY);
}

double Character::GetPitch() {
	return D3DXToRadian(this->CharacterData.rotX);
}

double Character::GetRoll() {
	return D3DXToRadian(this->CharacterData.rotZ);
}

int Character::GetHookCharacterDBId() {
	return this->CharacterData.hookCharacterDbId;
}

string Character::GetTextureFile() {
	return this->CharacterData.textureFile;
}

bool Character::IsPlayerCharacter() {
	return this->CharacterData.isPlayer;
}

bool Character::IsAlive() {
	if((this->CharacterData.hp <= 0 && !this->CharacterData.invulnerable) || (this->CharacterData.lifespan == 0)) return false;
	else {
		if(this->CharacterData.lifespan >= 0) this->CharacterData.lifespan--;
		return true;
	}
}

void Character::SetHookCharacterID(int pos) {
	this->hookCharacterId = pos;
}

void Character::SetVelocity(double xv, double yv) {
	this->CharacterData.xvel = xv;
	this->CharacterData.yvel = yv;
}

void Character::SetVelocityX(double xv) {
	this->CharacterData.xvel = xv;
}

void Character::SetVelocityY(double yv) {
	this->CharacterData.yvel = yv;
}

void Character::SetInvulnerable(bool inv) {
	this->CharacterData.invulnerable = true;
}

void Character::Move() {

	this->CharacterData.yvel -= gravity;

	if(this->hookCharacterId >= 0) {
		this->CharacterData.x += (Game::CharactersPtrs[this->hookCharacterId]->GetVX() + this->CharacterData.xvel) / (float)ticksPS;
		this->CharacterData.y += (Game::CharactersPtrs[this->hookCharacterId]->GetVY() + this->CharacterData.yvel) / (float)ticksPS;
	}
	else {
		this->CharacterData.x += this->CharacterData.xvel / (float)ticksPS;
		this->CharacterData.y += this->CharacterData.yvel / (float)ticksPS;
	}

	if(this->CharacterData.xvel > 0.0f) this->facingTo = Direction::Right;
	if(this->CharacterData.xvel < 0.0f) this->facingTo = Direction::Left;
}

void Character::MoveManually(double x, double y) {
	this->CharacterData.x += x;
	this->CharacterData.y += y;
}

void Character::UpdateAnimationDirection() {
	if(this->CharacterData.xvel > 0 && ((this->CharacterData.yvel > 0 && this->CharacterData.yvel <= this->CharacterData.xvel) || (this->CharacterData.yvel < 0 && -this->CharacterData.yvel <= this->CharacterData.xvel) || this->CharacterData.yvel == 0)) {
		this->animDirection = Right;
		return;
	}

	if(this->CharacterData.xvel < 0 && ((this->CharacterData.yvel > 0 && -this->CharacterData.yvel >= this->CharacterData.xvel) || (this->CharacterData.yvel < 0 && this->CharacterData.yvel >= this->CharacterData.xvel) || this->CharacterData.yvel == 0)) {
		this->animDirection = Left;
		return;
	}

	if(this->CharacterData.yvel > 0 && ((this->CharacterData.xvel > 0 && this->CharacterData.xvel <= this->CharacterData.yvel) || (this->CharacterData.xvel < 0 && -this->CharacterData.xvel <= this->CharacterData.yvel) || this->CharacterData.xvel == 0)) {
		this->animDirection = Up;
		return;
	}

	if(this->CharacterData.yvel < 0 && ((this->CharacterData.xvel > 0 && -this->CharacterData.xvel >= this->CharacterData.yvel) || (this->CharacterData.xvel < 0 && this->CharacterData.xvel >= this->CharacterData.yvel) || this->CharacterData.xvel == 0)) {
		this->animDirection = Down;
		return;
	}

	this->animDirection = NotMoving;
	return;
}

void Character::NextAnimationFrame() {

	if(!this->CharacterData.isAnimated) return;

	this->UpdateAnimationDirection();

	if(runningAnimationDirection != this->animDirection) {
		// Reset animation with new direction
		this->lastFrame = 51;
		this->animationStep = 0;
		this->runningAnimationDirection = this->animDirection;
	}
	else {

		if(this->CharacterData.ticksPerFrame >= this->lastFrame)  {
			this->lastFrame++;
			return;
		}
		else lastFrame = 0;

		// Continue with current animation
		FLOAT stretchFact = (FLOAT)this->CharacterData.textureResolution / (FLOAT)this->CharacterData.width;

		FLOAT widthFactor = stretchFact * ((FLOAT)this->CharacterData.width / (FLOAT)this->CharacterData.textureWidth);
		FLOAT heightFactor = stretchFact * ((FLOAT)this->CharacterData.height / (FLOAT)this->CharacterData.textureHeight);

		if((widthFactor + widthFactor * this->animationStep) > 1.0f) this->animationStep = 0;

		this->CharacterQuad[0].U = 0.0f + this->animationStep * widthFactor;
		this->CharacterQuad[1].U = widthFactor + this->animationStep * widthFactor;
		this->CharacterQuad[2].U = 0.0f + this->animationStep * widthFactor;
		this->CharacterQuad[3].U = widthFactor + this->animationStep * widthFactor;

		switch(runningAnimationDirection) {
		case NotMoving:
			{
				this->CharacterQuad[0].V = 0.0f;
				this->CharacterQuad[1].V = 0.0f;
				this->CharacterQuad[2].V = heightFactor;
				this->CharacterQuad[3].V = heightFactor;
			} break;

		case Right:
			{
				this->CharacterQuad[0].V = heightFactor;
				this->CharacterQuad[1].V = heightFactor;
				this->CharacterQuad[2].V = 2 * heightFactor;
				this->CharacterQuad[3].V = 2 * heightFactor;
			} break;

		case Left:
			{
				this->CharacterQuad[0].V = 2 * heightFactor;
				this->CharacterQuad[1].V = 2 * heightFactor;
				this->CharacterQuad[2].V = 3 * heightFactor;
				this->CharacterQuad[3].V = 3 * heightFactor;
			} break;

		case Up:
			{
				this->CharacterQuad[0].V = 3 * heightFactor;
				this->CharacterQuad[1].V = 3 * heightFactor;
				this->CharacterQuad[2].V = 4 * heightFactor;
				this->CharacterQuad[3].V = 4 * heightFactor;
			} break;

		case Down:
			{
				this->CharacterQuad[0].V = 4 * heightFactor;
				this->CharacterQuad[1].V = 4 * heightFactor;
				this->CharacterQuad[2].V = 5 * heightFactor;
				this->CharacterQuad[3].V = 5 * heightFactor;
			} break;
		}

		this->animationStep++;
	}
}